package com.franklin.ideaplugin.easytesting.spring.invoke;

import cn.hutool.core.util.StrUtil;
import com.franklin.ideaplugin.easytesting.common.log.ILogger;
import com.franklin.ideaplugin.easytesting.common.log.LoggerFactory;
import com.franklin.ideaplugin.easytesting.core.invoke.interceptor.IScriptObjectPropertyFiller;
import com.franklin.ideaplugin.easytesting.spring.utils.SpringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.util.ReflectionUtils;

import javax.annotation.Resource;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Ye Junhui
 * @since 2023/8/1
 */
public class SpringScriptObjectPropertyFiller implements IScriptObjectPropertyFiller {

    private ILogger log = LoggerFactory.getLogger(SpringScriptObjectPropertyFiller.class);

    @Override
    public void fill(Object scriptObject) {
        ReflectionUtils.doWithFields(scriptObject.getClass(),field -> {
            try {
                ReflectionUtils.makeAccessible(field);
                if (!hasInjectAnn(field)){
                    return;
                }

                Object bean = null;

                String beanName = getBeanName(field);
                if (StrUtil.isNotBlank(beanName)){
                    bean = SpringUtil.getBean(beanName);
                }else {
                    Class<?> beanType = getBeanType(field);
                    if (isListBeanType(field)){
                        bean = new ArrayList<>(SpringUtil.getBeansOfType(beanType).values());
                    }else if (isMapBeanType(field)){
                        bean = SpringUtil.getBeansOfType(beanType);
                    }else {
                        bean = SpringUtil.getBean(beanType);
                    }
                }
                field.set(scriptObject,bean);
            } catch (Throwable e) {
                log.error("EasyTesting>>>> inject bean fail");
            }
        });
    }

    private boolean isListBeanType(Field field){
        Type genericType = field.getGenericType();
        if (genericType instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            Class rawType = (Class) parameterizedType.getRawType();
            return rawType.equals(List.class);
        }
        return false;
    }

    private boolean isMapBeanType(Field field){
        Type genericType = field.getGenericType();
        if (genericType instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            Class rawType = (Class) parameterizedType.getRawType();
            return rawType.equals(Map.class);
        }
        return false;
    }

    /**
     * 获取bean的类型
     * @param field
     * @return
     */
    private Class<?> getBeanType(Field field){
        Type genericType = field.getGenericType();
        if (genericType instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            return  ((Class) actualTypeArguments[0]);
        }
        return field.getType();
    }

    /**
     * 指定bean的名称
     * @param field
     * @return
     */
    private String getBeanName(Field field){
        Qualifier qualifier = field.getAnnotation(Qualifier.class);
        if (Objects.isNull(qualifier)){
            return "";
        }
        return qualifier.value();
    }

    /**
     * 是否有注入注解
     * @param field
     * @return
     */
    private boolean hasInjectAnn(Field field){
        Autowired autowired = field.getAnnotation(Autowired.class);
        if (Objects.nonNull(autowired)){
            return true;
        }
        Resource resource = field.getAnnotation(Resource.class);
        return Objects.nonNull(resource);
    }

}
